home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / kernel / rpc / rpcRecovery.c.old < prev    next >
Text File  |  1992-12-18  |  34KB  |  1,205 lines

  1. /* 
  2.  * rpcRecovery.c --
  3.  *
  4.  *    The routines here maintain up/down state about other hosts.
  5.  *    Recovery actions that are registered via Rpc_HostNotify are
  6.  *    called-back when a host crashes and when it reboots.
  7.  *    Regular message    traffic plus explicit pings are used to determine
  8.  *    the state of other hosts.  The main external procedures are
  9.  *    Rpc_HostIsDown, used to query the state of another host, and
  10.  *    Rpc_WaitForHost, used to block a process until a host reboots.
  11.  *    (Rpc_WaitForHost isn't used much.  Instead, modules rely on the
  12.  *    recovery callbacks to indicate that a host is back to life, and
  13.  *    they block processes in their own way.)
  14.  *
  15.  *    One impact of these recovery hooks on the RPC system is that service
  16.  *    requests from a client that is just rebooting are blocked until
  17.  *    the recovery actions complete.
  18.  *
  19.  * Copyright 1987 Regents of the University of California
  20.  * All rights reserved.
  21.  */
  22.  
  23. #ifndef lint
  24. static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/rpc/rpcRecovery.c.old,v 9.0 89/09/12 15:18:27 douglis Stable $ SPRITE (Berkeley)";
  25. #endif not lint
  26.  
  27.  
  28. #include "sprite.h"
  29. #include "rpc.h"
  30. #include "rpcInt.h"
  31. #include "sync.h"
  32. #include "hash.h"
  33. #include "mem.h"
  34. #include "trace.h"
  35.  
  36. /*
  37.  * The state of other hosts is kept in a hash table keyed on SpriteID.
  38.  * This state is maintained by RpcHostAlive and RpcHostDead, which are
  39.  * called in turn after packet reception or RPC timeout, respectively.
  40.  * RpcHostDead is also called by the Rpc_Daemon if it can't get an
  41.  * explicit acknowledgment from a client.
  42.  */
  43. static Hash_Table    recoveryHashTableStruct;
  44. static Hash_Table    *recovHashTable = &recoveryHashTableStruct;
  45.  
  46. typedef struct RecovHostState {
  47.     int            state;        /* flags defined below */
  48.     int            clientState;    /* flags defined in rpc.h */
  49.     int            spriteID;    /* Sprite Host ID */
  50.     int            bootID;        /* Timestamp from RPC header */
  51.     Time        time;        /* Time of last message */
  52.     Sync_Condition    alive;        /* Notified when host comes up */
  53.     Sync_Condition    recovery;    /* Notified when recovery is complete */
  54. } RecovHostState;
  55.  
  56. /*
  57.  * Access to the hash table is monitored.
  58.  */
  59. Sync_Lock rpcRecoveryLock;
  60. #define LOCKPTR (&rpcRecoveryLock)
  61.  
  62. /*
  63.  * Host state:
  64.  *    RECOV_STATE_UNKNOWN    Initial state.
  65.  *    RECOV_HOST_ALIVE    Set when we receive a message from the host
  66.  *    RECOV_HOST_DEAD        Set when an RPC times out.
  67.  *
  68.  *    RECOV_CRASH_CALLBACKS    Set during the crash call-backs, this is used
  69.  *                to block RPC server processes until the
  70.  *                crash recovery actions have completed.
  71.  *    RECOV_HOST_PINGING    Set while there are pinging call-backs scheduled
  72.  *    RECOV_HOST_BOOTING    Set while there are pinging call-backs scheduled
  73.  *
  74.  *    RECOV_WAITING        artificial state to trace Rpc_WaitForHost
  75.  *    RECOV_CRASH        artificial state to trace RpcCrashCallBacks
  76.  *    RECOV_REBOOT        artificial state to trace RpcRebootCallBacks
  77.  */
  78. #define RECOV_STATE_UNKNOWN    0x0
  79. #define RECOV_HOST_ALIVE    0x1
  80. #define RECOV_HOST_DEAD        0x2
  81.  
  82. #define RECOV_CRASH_CALLBACKS    0x0100
  83. #define RECOV_HOST_PINGING    0x0200
  84. #define RECOV_HOST_BOOTING    0x0400
  85.  
  86. #define RECOV_WAITING        0x4
  87. #define RECOV_CRASH        0x8
  88. #define RECOV_REBOOT        0x10
  89.  
  90. /*
  91.  * A host is "pinged" (to see when it reboots) at an interval determined by
  92.  * rpcPingSeconds.
  93.  */
  94. int rpcPingSeconds = 30;
  95.  
  96. /*
  97.  * After a host reboots we pause a bit before attempting recovery.  This
  98.  * allows a host to complete boot-time start up.  If we don't pause the
  99.  * ping done by the recovery call backs may fail and we may erroneously
  100.  * think that the other guy crashed right away.
  101.  */
  102. int rpcRecoveryPause = 30;    /* Seconds */
  103.  
  104. /*
  105.  * Other kernel modules can arrange call-backs when a host reboots.
  106.  * The following list structure is used to keep these.  The calling
  107.  * sequence of the callback is as follows:
  108.  *    (*proc)(spriteID, clientData, when)
  109.  * where 'when' is RPC_WHEN_HOST_DOWN or RPC_WHEN_HOST_REBOOTS (never both).
  110.  */
  111.  
  112. typedef struct {
  113.     List_Links    links;
  114.     void    (*proc)();
  115.     int        flags;        /* RPC_WHEN_HOST_DOWN, RPC_WHEN_HOST_REBOOTS */
  116.     ClientData    clientData;
  117. } NotifyElement;
  118.  
  119. List_Links    rpcNotifyList;
  120.  
  121. /*
  122.  * A trace is kept for debugging/understanding the host state transisions.
  123.  */
  124. typedef struct RpcRecovTraceRecord {
  125.     int        spriteID;        /* Host ID whose state changed */
  126.     int        state;            /* Their new state */
  127. } RpcRecovTraceRecord;
  128.  
  129. /*
  130.  * Tracing events, these describe the trace record.  Note that some
  131.  *    trace types are defined in rpc.h for use with Rpc_HostTrace.
  132.  *
  133.  *    RECOV_CUZ_WAIT        Wait in Rpc_WaitForHost
  134.  *    RECOV_CUZ_WAKEUP    Wakeup in Rpc_WaitForHost
  135.  *    RECOV_CUZ_INIT        First time we were interested in the host
  136.  *    RECOV_CUZ_REBOOT    We detected a reboot
  137.  *    RECOV_CUZ_CRASH        We detected a crash
  138.  *    RECOV_CUZ_DONE        Recovery actions completed
  139.  *    RECOV_CUZ_PING_CHK    We are pinging the host to check it out
  140.  *    RECOV_CUZ_PING_ASK    We are pinging the host because we were asked
  141.  */
  142. #define RECOV_CUZ_WAIT        0x1
  143. #define RECOV_CUZ_WAKEUP    0x2
  144. #define RECOV_CUZ_INIT        0x4
  145. #define RECOV_CUZ_REBOOT    0x8
  146. #define RECOV_CUZ_CRASH        0x10
  147. #define RECOV_CUZ_DONE        0x20
  148. #define RECOV_CUZ_PING_CHK    0x40
  149. #define RECOV_CUZ_PING_ASK    0x80
  150.  
  151. Trace_Header rpcRecovTraceHdr;
  152. Trace_Header *rpcRecovTraceHdrPtr = &rpcRecovTraceHdr;
  153. int rpcRecovTraceLength = 50;
  154. Boolean rpcRecovTracing = TRUE;
  155.  
  156. #ifndef CLEAN
  157.  
  158. #define RECOV_TRACE(zspriteID, zstate, event) \
  159.     if (rpcRecovTracing) {\
  160.     RpcRecovTraceRecord rec;\
  161.     rec.spriteID = zspriteID;\
  162.     rec.state = zstate;\
  163.     Trace_Insert(rpcRecovTraceHdrPtr, event, &rec);\
  164.     }
  165. #else
  166.  
  167. #define RECOV_TRACE(zspriteID, zstate, event)
  168.  
  169. #endif not CLEAN
  170. /*
  171.  * Forward declarations.
  172.  */
  173. void RpcRebootCallBacks();
  174. void RpcCrashCallBacks();
  175. void MarkRecoveryComplete();
  176. int  GetHostState();
  177. void StartPinging();
  178. void CheckHost();
  179. void StopPinging();
  180.  
  181.  
  182. /*
  183.  *----------------------------------------------------------------------
  184.  *
  185.  * RpcInitRecovery --
  186.  *
  187.  *    Set up the data structures used by the RpcRecovery module.
  188.  *
  189.  * Results:
  190.  *    None.
  191.  *
  192.  * Side effects:
  193.  *    None.
  194.  *
  195.  *----------------------------------------------------------------------
  196.  */
  197.  
  198. void
  199. RpcInitRecovery()
  200. {
  201.     Hash_Init(recovHashTable, 8, HASH_ONE_WORD_KEYS);
  202.     List_Init(&rpcNotifyList);
  203.     Trace_Init(rpcRecovTraceHdrPtr, rpcRecovTraceLength,
  204.         sizeof(RpcRecovTraceRecord), 0);
  205. }
  206.  
  207. /*
  208.  *----------------------------------------------------------------------
  209.  *
  210.  * Rpc_HostNotify --
  211.  *
  212.  *    Add a call-back for other modules to use when a host crashes/reboots.
  213.  *    The 'when' parameter specifies when to callback the client procedure.
  214.  *    If RPC_WHEN_HOST_DOWN then the procedure is called when the RPC
  215.  *    module has gotten a timeout trying to reach the host.  If it is
  216.  *    RPC_WHEN_HOST_REBOOTS then the call-back is made when the RPC
  217.  *    module detects a reboot due to the bootID changing.  If both
  218.  *    are specified then the call-back is made at both times.
  219.  *    
  220.  * Results:
  221.  *    None.
  222.  *
  223.  * Side effects:
  224.  *    Entry added to notify list.
  225.  *
  226.  *----------------------------------------------------------------------
  227.  */
  228. void
  229. Rpc_HostNotify(proc, clientData, when)
  230.     void    (*proc)();
  231.     ClientData    clientData;
  232.     int        when;        /* RPC_WHEN_HOST_DOWN, RPC_WHEN_HOST_REBOOTS */
  233. {
  234.     register    NotifyElement    *notifyPtr;
  235.  
  236.     notifyPtr = (NotifyElement *) Mem_Alloc(sizeof(NotifyElement));
  237.     notifyPtr->proc = proc;
  238.     notifyPtr->clientData = clientData;
  239.     notifyPtr->flags = when;
  240.     List_InitElement((List_Links *) notifyPtr);
  241.     List_Insert((List_Links *) notifyPtr, LIST_ATREAR(&rpcNotifyList));
  242. }
  243.  
  244. /*
  245.  *----------------------------------------------------------------------
  246.  *
  247.  * Rpc_HostIsDown --
  248.  *
  249.  *    This decides if the specified host is down, and will make sure
  250.  *    that the host is being "pinged" if the caller wants to find
  251.  *    out (via the callbacks setup in Rpc_HostNotify) when the host
  252.  *    comes back to life.  If the host is known to be down this routine
  253.  *    returns TRUE and makes sure pinging is initiated (if needed).
  254.  *    Otherwise, if there hasn't been recent message traffic 
  255.  *    (within the last 10 seconds) then this will ping the host to find
  256.  *    out if it's still up.  There are two cases then, the host isn't
  257.  *    up, or it is booting but it's RPC service is not ready yet.
  258.  *    We return FALSE so that our caller doesn't think the host
  259.  *    has crashed
  260.  *
  261.  * Results:
  262.  *    SUCCESS if the host is up, FAILURE if it doesn't respond to
  263.  *    pings or is known to be down, and RPC_SERVICE_DISABLED if
  264.  *    the host says so.
  265.  *
  266.  * Side effects:
  267.  *    May do a ping.  If the 'ping' parameter is TRUE this will make
  268.  *    sure that pinging is in progress if the host is down.
  269.  *
  270.  *----------------------------------------------------------------------
  271.  */
  272.  
  273. ReturnStatus
  274. Rpc_HostIsDown(spriteID, ping)
  275.     int spriteID;
  276.     Boolean ping;    /* If TRUE, we make sure the host is being pinged
  277.              * if it is down now */
  278. {
  279.     register ReturnStatus status = SUCCESS;
  280.  
  281.     if (spriteID == NET_BROADCAST_HOSTID) {
  282.     Sys_Panic(SYS_WARNING, "Rpc_HostIsDown, got broadcast address\n");
  283.     return(SUCCESS);
  284.     }
  285.     switch (GetHostState(spriteID)) {
  286.     case RECOV_STATE_UNKNOWN:
  287.         RECOV_TRACE(spriteID, RECOV_STATE_UNKNOWN, RECOV_CUZ_PING_ASK);
  288.         status = Rpc_Ping(spriteID);
  289.         break;
  290.     case RECOV_HOST_ALIVE:
  291.         status = SUCCESS;
  292.         break;
  293.     case RECOV_HOST_DEAD:
  294.         status = FAILURE;
  295.         break;
  296.     }
  297.     if (status != SUCCESS && ping) {
  298.     StartPinging(spriteID);
  299.     }
  300.     return(status);
  301. }
  302.  
  303. /*
  304.  *----------------------------------------------------------------------
  305.  *
  306.  * Rpc_WaitForHost --
  307.  *
  308.  *    Block the current process (at an interruptable priority) until
  309.  *    the given host comes back up.  This is used when retrying
  310.  *    filesystem operations when a fileserver goes down, for example.
  311.  *
  312.  * Results:
  313.  *    TRUE if the wait was interrupted.
  314.  *
  315.  * Side effects:
  316.  *    The current process is blocked
  317.  *    until messages from the host indicate it is up.
  318.  *
  319.  *----------------------------------------------------------------------
  320.  */
  321. Boolean
  322. Rpc_WaitForHost(spriteID)
  323.     int spriteID;            /* Host to monitor */
  324. {
  325.     /*
  326.      * Set up the hosts state (dead or alive) by pinging it.
  327.      * If it's down we drop into a monitored routine to do 
  328.      * the actual waiting.  It will check again to make sure
  329.      * we don't sleep on an alive host.
  330.      */
  331.     if (Rpc_HostIsDown(spriteID, TRUE) == FAILURE) {
  332.     RECOV_TRACE(spriteID, RECOV_STATE_UNKNOWN, RECOV_CUZ_WAIT);
  333.     return(RpcWaitForHostInt(spriteID));
  334.     } else {
  335.     return(FALSE);
  336.     }
  337. }
  338.  
  339. /*
  340.  *----------------------------------------------------------------------
  341.  *
  342.  * RpcWaitForHostInt --
  343.  *
  344.  *    Block the current process (at an interruptable priority) until
  345.  *    the given host comes back up.  Our caller should have already
  346.  *    probed to host with Rpc_HostIsDown so that pinging is already
  347.  *    initiated.
  348.  *
  349.  * Results:
  350.  *    TRUE is the wait was interrupted by a signal.
  351.  *
  352.  * Side effects:
  353.  *    If the host is thought down, the current process is blocked
  354.  *    until messages from the host indicate it is up.
  355.  *
  356.  *----------------------------------------------------------------------
  357.  */
  358. ENTRY Boolean
  359. RpcWaitForHostInt(spriteID)
  360.     int spriteID;            /* Host to monitor */
  361. {
  362.     Hash_Entry *hashPtr;
  363.     RecovHostState *hostPtr;
  364.     Boolean sigPending = FALSE;
  365.  
  366.     LOCK_MONITOR;
  367.  
  368.     if (spriteID <= 0 || spriteID == rpc_SpriteID) {
  369.     Sys_Panic(SYS_FATAL, "RpcWaitForHostInt, bad hostID %d\n", spriteID);
  370.     UNLOCK_MONITOR;
  371.     return(FALSE);
  372.     }
  373.  
  374.     hashPtr = Hash_Find(recovHashTable, spriteID);
  375.     if (hashPtr->value == (Address)NIL) {
  376.     Sys_Panic(SYS_FATAL, "RpcWaitForHostInt, no host state\n");
  377.     UNLOCK_MONITOR;
  378.     return;
  379.     } else {
  380.     hostPtr = (RecovHostState *)hashPtr->value;
  381.     }
  382.     while (!sigPending && (hostPtr->state & RECOV_HOST_DEAD)) {
  383.     sigPending = Sync_Wait(&hostPtr->alive, TRUE);
  384.     }
  385.     RECOV_TRACE(hostPtr->spriteID, hostPtr->state, RECOV_CUZ_WAKEUP);
  386.  
  387.     UNLOCK_MONITOR;
  388.     return(sigPending);
  389. }
  390.  
  391. /*
  392.  *----------------------------------------------------------------------
  393.  *
  394.  * Rpc_HostPrint --
  395.  *
  396.  *    Print out a statement concerning a host.  This maps to a
  397.  *    string hostname if possible, and prints out the message.
  398.  *    The strings come from netAddresses.c (but should be imported
  399.  *    via Net_RouteInstall system call).
  400.  *
  401.  *    MOVE THIS TO NET
  402.  *
  403.  * Results:
  404.  *    None.
  405.  *
  406.  * Side effects:
  407.  *    Sys_Printf.
  408.  *
  409.  *----------------------------------------------------------------------
  410.  */
  411.  
  412. void
  413. Rpc_HostPrint(spriteID, string)
  414.     int spriteID;
  415.     char *string;
  416. {
  417.     char *hostName;
  418.  
  419.     Net_SpriteIDToName(spriteID, &hostName);
  420.     if (hostName == (char *)NIL) {
  421.     Sys_Printf("Sprite Host <%d> %s\n", spriteID, string);
  422.     } else {
  423.     Sys_Printf("Sprite Host %s (%d) %s\n", hostName, spriteID, string);
  424.     }
  425. }
  426.  
  427. /*
  428.  *----------------------------------------------------------------------
  429.  *
  430.  * Rpc_HostTrace --
  431.  *
  432.  *    Add an entry to the rpc recovery trace.
  433.  *
  434.  * Results:
  435.  *    None.
  436.  *
  437.  * Side effects:
  438.  *    None.
  439.  *
  440.  *----------------------------------------------------------------------
  441.  */
  442.  
  443. ENTRY void
  444. Rpc_HostTrace(spriteID, event)
  445.     int spriteID;
  446.     int event;
  447. {
  448.     LOCK_MONITOR;
  449.  
  450.     RECOV_TRACE(spriteID, RECOV_STATE_UNKNOWN, event);
  451.  
  452.     UNLOCK_MONITOR;
  453. }
  454.  
  455. /*
  456.  *----------------------------------------------------------------------
  457.  *
  458.  * Rpc_HostGetState --
  459.  *
  460.  *    Return the client state associated with a host.  The recovery host
  461.  *    table is a convenient object keyed on spriteID.  Other modules can
  462.  *    set their own state in the table (beyond the simple up/down state
  463.  *    mainted by the rest of this module), and retrieve it with this call.
  464.  *
  465.  * Results:
  466.  *    A copy of the clientState field.  0 is returned if there is no
  467.  *    host table entry.
  468.  *
  469.  * Side effects:
  470.  *    None.
  471.  *
  472.  *----------------------------------------------------------------------
  473.  */
  474.  
  475. ENTRY int
  476. Rpc_HostGetState(spriteID)
  477.     int spriteID;
  478. {
  479.     Hash_Entry *hashPtr;
  480.     RecovHostState *hostPtr;
  481.     int result = 0;
  482.  
  483.     LOCK_MONITOR;
  484.  
  485.     hashPtr = Hash_LookOnly(recovHashTable, spriteID);
  486.     if (hashPtr != (Hash_Entry *)NIL) {
  487.     hostPtr = (RecovHostState *)hashPtr->value;
  488.     if (hostPtr != (RecovHostState *)NIL) {
  489.         result = hostPtr->clientState;
  490.     }
  491.     }
  492.     UNLOCK_MONITOR;
  493.     return(result);
  494. }
  495.  
  496. /*
  497.  *----------------------------------------------------------------------
  498.  *
  499.  * Rpc_HostSetState --
  500.  *
  501.  *    Set the client state associated with a host.  This completely
  502.  *    overwrites the previous value of the client state.
  503.  *
  504.  * Results:
  505.  *    None.
  506.  *
  507.  * Side effects:
  508.  *    Sets the clientState field of the host state.  This will add an
  509.  *    entry to the host table if one doesn't alreay exist.  Its RPC
  510.  *    up/down state is set to "unknown" in this case.
  511.  *
  512.  *----------------------------------------------------------------------
  513.  */
  514.  
  515. ENTRY void
  516. Rpc_HostSetState(spriteID, state)
  517.     int spriteID;
  518.     int state;
  519. {
  520.     Hash_Entry *hashPtr;
  521.     RecovHostState *hostPtr;
  522.  
  523.     LOCK_MONITOR;
  524.  
  525.     hashPtr = Hash_Find(recovHashTable, spriteID);
  526.     hostPtr = (RecovHostState *)hashPtr->value;
  527.     if (hostPtr == (RecovHostState *)NIL) {
  528.     hostPtr = Mem_New(RecovHostState);
  529.     hashPtr->value = (Address)hostPtr;
  530.  
  531.     Byte_Zero(sizeof(RecovHostState), (Address)hostPtr);
  532.     hostPtr->state = RECOV_STATE_UNKNOWN;
  533.     hostPtr->spriteID = spriteID;
  534.     }
  535.     hostPtr->clientState = state;
  536.     UNLOCK_MONITOR;
  537. }
  538.  
  539. /*
  540.  *----------------------------------------------------------------------
  541.  *
  542.  * RpcHostAlive --
  543.  *
  544.  *    Mark the host as being alive.  This is called when we've received
  545.  *    a message from the host.  It uses state from the host table and
  546.  *    the bootID parameter to detect reboots.  If a reboot is detected
  547.  *    but we thought the host was up then the Crash call-backs are invoked.
  548.  *    In any case, a reboot invokes the Reboot call-backs.  (Call-backs
  549.  *    are installed with Rpc_HostNotify.)  Finally, a time stamp is
  550.  *    kept so we can check when we last got a message from a host.
  551.  *
  552.  *    This procedure is called from client RPC upon successful completion
  553.  *    of an RPC, and by server RPC upon reciept of a client request.
  554.  *    These two cases are identified by the 'asyncRecovery' parameter.
  555.  *    Servers want synchronous recovery so they don't service anything
  556.  *    until state associated with that client has been cleaned up via
  557.  *    the Crash call-backs.  So RpcHostAlive blocks (if !asyncRecovery)
  558.  *    until the crash call-backs are complete.  Clients don't have the
  559.  *    same worries so they let the crash call-backs complete in the
  560.  *    background (asyncRecovery is TRUE).
  561.  *
  562.  * Results:
  563.  *    None.
  564.  *
  565.  * Side effects:
  566.  *    Updates the boot timestamp of the other host.  Procedures installed
  567.  *    with Rpc_HostNotify are called when the bootID changes.  A timestamp
  568.  *    of when this message was received is obtained from the "cheap" clock
  569.  *    so we can tell later if there has been recent message traffic.
  570.  *
  571.  *----------------------------------------------------------------------
  572.  */
  573.  
  574. ENTRY void
  575. RpcHostAlive(spriteID, bootID, asyncRecovery)
  576.     int spriteID;        /* Host ID of the message sender */
  577.     int bootID;            /* Boot time stamp from message header */
  578.     Boolean asyncRecovery;    /* TRUE means do recovery call-backs in
  579.                  * the background. FALSE causes the process
  580.                  * to wait until crash recovery is complete. */
  581. {
  582.     register Hash_Entry *hashPtr;
  583.     register RecovHostState *hostPtr;
  584.     Boolean reboot = FALSE;    /* Used to control print statements at reboot */
  585.  
  586.     LOCK_MONITOR;
  587.     if (spriteID == NET_BROADCAST_HOSTID || bootID == 0) {
  588.     /*
  589.      * Don't track the broadcast address.  Also ignore zero valued
  590.      * bootIDs.  These come from hosts at early boot time, or
  591.      * in certain error conditions like trying to send too much
  592.      * data in a single RPC.
  593.      */
  594.     UNLOCK_MONITOR;
  595.     return;
  596.     }
  597.  
  598.     hashPtr = Hash_Find(recovHashTable, spriteID);
  599.     if (hashPtr->value == (Address)NIL) {
  600.     /*
  601.      * Initialize the host's state. This is the first time we've talked
  602.      * to it since we've been up, so take no action.
  603.      */
  604.     hostPtr = Mem_New(RecovHostState);
  605.     hashPtr->value = (Address)hostPtr;
  606.  
  607.     Byte_Zero(sizeof(RecovHostState), (Address)hostPtr);
  608.     hostPtr->state = RECOV_HOST_ALIVE;
  609.     hostPtr->spriteID = spriteID;
  610.     hostPtr->bootID = bootID;
  611.  
  612.     Rpc_HostPrint(spriteID, "is up");
  613.     RECOV_TRACE(spriteID, RECOV_HOST_ALIVE, RECOV_CUZ_INIT);
  614.     } else {
  615.     hostPtr = (RecovHostState *)hashPtr->value;
  616.     }
  617.     if (hostPtr != (RecovHostState *)NIL) {
  618.     /*
  619.      * Have to read the clock in order to suppress repeated pings,
  620.      * see GetHostState and Rpc_HostIsDown.
  621.      */
  622.     Timer_GetTimeOfDay(&hostPtr->time, (int *)NIL, (Boolean *)NIL);
  623.     /*
  624.      * Check for a rebooted peer by comparing boot time stamps.
  625.      * The first process to detect this initiates recovery actions.
  626.      */
  627.     if (hostPtr->bootID != bootID) {
  628.         Rpc_HostPrint(spriteID, "rebooted");
  629.         hostPtr->bootID = bootID;
  630.         reboot = TRUE;
  631.         RECOV_TRACE(spriteID, hostPtr->state, RECOV_CUZ_REBOOT);
  632.         if (hostPtr->state & RECOV_HOST_ALIVE) {
  633.         /*
  634.          * A crash occured un-detected.  We do the crash call-backs
  635.          * first, and block server processes in the meantime.
  636.          * RECOV_CRASH_CALLBACKS flag is reset by RpcCrashCallBacks.
  637.          * The host is marked dead here so we fall into the
  638.          * switch below and call the reboot callbacks.
  639.          */
  640.         RECOV_TRACE(spriteID, RECOV_CRASH, RECOV_CUZ_REBOOT);
  641.         hostPtr->state &= ~RECOV_HOST_ALIVE;
  642.         hostPtr->state |= (RECOV_HOST_DEAD | RECOV_CRASH_CALLBACKS);
  643.         Proc_CallFunc(RpcCrashCallBacks, spriteID, 0);
  644.         }
  645.     }
  646.     /*
  647.      * Block servers until crash recovery actions complete.
  648.      * Servers are synchronous with respect to reboot recovery.
  649.      * This blocks requests from clients until after the
  650.      * recovery actions complete.
  651.      */
  652.     if (! asyncRecovery) {
  653.         while (hostPtr->state & RECOV_CRASH_CALLBACKS) {
  654.         Sync_Wait(&hostPtr->recovery, FALSE);
  655.         if (sys_ShuttingDown) {
  656.             Sys_Printf("Warning, Server exiting from RpcHostAlive\n");
  657.             Proc_Exit(1);
  658.         }
  659.         }
  660.     }
  661.     /*
  662.      * Now that we've taken care of crash recovery, we see if the host
  663.      * is newly up.  If so, invoke the reboot call-backs and then notify
  664.      * waiting processes. This means clientA (us) may start
  665.      * re-opening files from serverB (the other guy) at the same time
  666.      * as clientA (us) is closing files that serverB had had open.
  667.      * ie. both the crash and reboot call backs may proceed in parallel.
  668.      */
  669.     switch(hostPtr->state & ~(RECOV_CRASH_CALLBACKS|RECOV_HOST_PINGING)) {
  670.         case RECOV_HOST_ALIVE:
  671.         /*
  672.          * Host already alive.
  673.          */
  674.         break;
  675.         case RECOV_HOST_DEAD: {
  676.         register int wait;
  677.         /*
  678.          * Notify interested parties that the host is up.  If the host
  679.          * has done a full reboot we wait a bit before pounding on
  680.          * it with our re-open requests.  This gives it a chance
  681.          * to create RPC server processes, etc. so we don't think
  682.          * it crashed because we tried to talk to it too soon.
  683.          */
  684.         if ( !reboot ) {
  685.             Rpc_HostPrint(spriteID, "is back again");
  686.             wait = 0;
  687.         } else {
  688.             wait = timer_IntOneSecond * rpcRecoveryPause;
  689.         }
  690.         hostPtr->state &= ~RECOV_HOST_DEAD;
  691.         hostPtr->state |= RECOV_HOST_ALIVE;
  692.         Sync_Broadcast(&hostPtr->alive)
  693.         Proc_CallFunc(RpcRebootCallBacks, spriteID, wait);
  694.         break;
  695.         default:
  696.         Sys_Panic(SYS_WARNING, "Unexpected state <%x> for ",
  697.             hostPtr->state);
  698.         Rpc_HostPrint(spriteID, "");
  699.         break;
  700.         }
  701.     }
  702.     }
  703.     UNLOCK_MONITOR;
  704. }
  705.  
  706. /*
  707.  *----------------------------------------------------------------------
  708.  *
  709.  * RpcHostDead --
  710.  *
  711.  *    Change the host's state to "dead".  This is called from client RPC
  712.  *    when an RPC timed out with no response.  It is also called by the
  713.  *    Rpc_Daemon when it can't recontact a client to get an explicit
  714.  *    acknowledgment.
  715.  *
  716.  * Results:
  717.  *    None.
  718.  *
  719.  * Side effects:
  720.  *    Sets the state in the host state table to dead.  Pings are not
  721.  *    initiated here because we may or may not be interested in
  722.  *    the other host.  See Rpc_HostIsDown.
  723.  *
  724.  *----------------------------------------------------------------------
  725.  */
  726.  
  727. ENTRY void
  728. RpcHostDead(spriteID)
  729.     int spriteID;
  730. {
  731.     register Hash_Entry *hashPtr;
  732.     register RecovHostState *hostPtr;
  733.  
  734.     LOCK_MONITOR;
  735.     if (spriteID == NET_BROADCAST_HOSTID || rpc_NoTimeouts) {
  736.     /*
  737.      * If rpcNoTimeouts is set the Rpc_Daemon may still call us if
  738.      * it can't get an acknowledgment from a host to close down
  739.      * a connection.  We ignore this so that we don't take action
  740.      * against the offending host (who is probably in the debugger)
  741.      */
  742.     UNLOCK_MONITOR;
  743.     return;
  744.     }
  745.  
  746.     hashPtr = Hash_LookOnly(recovHashTable, spriteID);
  747.     if (hashPtr != (Hash_Entry *)NIL) {
  748.     hostPtr = (RecovHostState *)hashPtr->value;
  749.     if (hostPtr != (RecovHostState *)NIL) {
  750.         switch(hostPtr->state & ~(RECOV_CRASH_CALLBACKS|
  751.                       RECOV_HOST_PINGING)) {
  752.         case RECOV_HOST_DEAD:
  753.             /*
  754.              * Host already dead.
  755.              */
  756.             break;
  757.         case RECOV_STATE_UNKNOWN:
  758.         case RECOV_HOST_ALIVE:
  759.             hostPtr->state &= ~(RECOV_HOST_ALIVE|RECOV_STATE_UNKNOWN);
  760.             hostPtr->state |= RECOV_HOST_DEAD|RECOV_CRASH_CALLBACKS;
  761.             Rpc_HostPrint(spriteID, "is down");
  762.             RECOV_TRACE(spriteID, hostPtr->state, RECOV_CUZ_CRASH);
  763.             Proc_CallFunc(RpcCrashCallBacks, spriteID, 0);
  764.             break;
  765.         }
  766.     }
  767.     }
  768.     UNLOCK_MONITOR;
  769. }
  770.  
  771. /*
  772.  *----------------------------------------------------------------------
  773.  *
  774.  * RpcRebootCallBacks --
  775.  *
  776.  *    This calls the call-back procedures installed by other modules
  777.  *    via Rpc_HostNotify.  It is invoked asynchronously from RpcHostAlive
  778.  *    when that procedure detects a reboot.  It does an explict ping
  779.  *    of the other host to make sure it is ready for our recovery actions.
  780.  *    This will reschedule itself for later if the host isn't ready.
  781.  *
  782.  * Results:
  783.  *    None.
  784.  *
  785.  * Side effects:
  786.  *    Invoke the call-backs.
  787.  *
  788.  *----------------------------------------------------------------------
  789.  */
  790.  
  791. void
  792. RpcRebootCallBacks(data, callInfoPtr)
  793.     ClientData data;
  794.     Proc_CallInfo *callInfoPtr;
  795. {
  796.     ReturnStatus status;
  797.     register NotifyElement *notifyPtr;
  798.     register int spriteID = (int)data;
  799.  
  800.     status = Rpc_Ping(spriteID);
  801.     switch(status) {
  802.     case RPC_SERVICE_DISABLED:
  803.         Rpc_HostPrint(spriteID, "still booting");
  804.         callInfoPtr->interval = rpcRecoveryPause * timer_IntOneSecond;
  805.         break;
  806.     case RPC_TIMEOUT:
  807.         Rpc_HostPrint(spriteID, "not responding");
  808.         callInfoPtr->interval = rpcRecoveryPause * timer_IntOneSecond;
  809.         break;
  810.     case SUCCESS:
  811.         LIST_FORALL(&rpcNotifyList, (List_Links *)notifyPtr) {
  812.         if (notifyPtr->flags & RPC_WHEN_HOST_REBOOTS) {
  813.             (*notifyPtr->proc)(spriteID, notifyPtr->clientData,
  814.                          RPC_WHEN_HOST_REBOOTS);
  815.          }
  816.         }
  817.         RECOV_TRACE(spriteID, RECOV_REBOOT, RECOV_CUZ_DONE);
  818.         callInfoPtr->interval = 0;    /* Don't call again */
  819.         break;
  820.     }
  821. }
  822.  
  823. /*
  824.  *----------------------------------------------------------------------
  825.  *
  826.  * RpcCrashCallBacks --
  827.  *
  828.  *    Invoked asynchronously from RpcHostDead so that other modules
  829.  *    can clean up behind the crashed host.  When done the host
  830.  *    is marked as having recovery complete.  This unblocks server
  831.  *    processes stalled in RpcHostAlive.
  832.  *
  833.  * Results:
  834.  *    None.
  835.  *
  836.  * Side effects:
  837.  *    Invoke the call-backs with the RPC_WHEN_HOST_DOWN flag.
  838.  *    Clears the recovery in progress flag checked in RpcHostAlive.
  839.  *
  840.  *----------------------------------------------------------------------
  841.  */
  842.  
  843. void
  844. RpcCrashCallBacks(data, callInfoPtr)
  845.     ClientData data;
  846.     Proc_CallInfo *callInfoPtr;
  847. {
  848.     register NotifyElement *notifyPtr;
  849.     register int spriteID = (int)data;
  850.  
  851.     LIST_FORALL(&rpcNotifyList, (List_Links *)notifyPtr) {
  852.     if (notifyPtr->flags & RPC_WHEN_HOST_DOWN) {
  853.         (*notifyPtr->proc)(spriteID, notifyPtr->clientData,
  854.                      RPC_WHEN_HOST_DOWN);
  855.      }
  856.     }
  857.     MarkRecoveryComplete(spriteID);
  858.     RECOV_TRACE(spriteID, RECOV_CRASH, RECOV_CUZ_DONE);
  859.     callInfoPtr->interval = 0;    /* Don't call again */
  860. }
  861.  
  862. /*
  863.  *----------------------------------------------------------------------
  864.  *
  865.  * MarkRecoveryComplete --
  866.  *
  867.  *    The recovery call-backs have completed, and this procedure's
  868.  *    job is to mark that fact in the host hash table and to notify
  869.  *    any processes that are blocked in RpcHostAlive waiting for this.
  870.  *
  871.  * Results:
  872.  *    None.
  873.  *
  874.  * Side effects:
  875.  *    Sets the state, if any, in the host state table.
  876.  *    Notifies the hostPtr->recovery condition
  877.  *
  878.  *----------------------------------------------------------------------
  879.  */
  880.  
  881. ENTRY static void
  882. MarkRecoveryComplete(spriteID)
  883. {
  884.     register Hash_Entry *hashPtr;
  885.     register RecovHostState *hostPtr;
  886.  
  887.     LOCK_MONITOR;
  888.  
  889.     hashPtr = Hash_LookOnly(recovHashTable, spriteID);
  890.     if (hashPtr != (Hash_Entry *)NIL) {
  891.     hostPtr = (RecovHostState *)hashPtr->value;
  892.     if (hostPtr != (RecovHostState *)NIL) {
  893.         hostPtr->state &= ~RECOV_CRASH_CALLBACKS;
  894.         Sync_Broadcast(&hostPtr->recovery);
  895.     }
  896.     }
  897.     UNLOCK_MONITOR;
  898. }
  899.  
  900. /*
  901.  *----------------------------------------------------------------------
  902.  *
  903.  * GetHostState --
  904.  *
  905.  *    This looks into    the host table to see and provides a guess
  906.  *    as to the host's current state.  It uses a timestamp kept in
  907.  *    the host state to see if there's been recent message traffic.
  908.  *    If so, RECOV_HOST_ALIVE is returned.  If not, RECOV_STATE_UNKNOWN
  909.  *    is returned and the caller should ping to make sure.  Finally,
  910.  *    if it is known that the host is down already, then RECOV_HOST_DEAD
  911.  *    is returned.
  912.  *
  913.  * Results:
  914.  *    RECOV_STATE_UNKNOWN if the caller should ping to make sure.
  915.  *    RECOV_HOST_ALIVE if the host is up (recent message traffic).
  916.  *    RECOV_HOST_DEAD if the host is down (recent timeouts).
  917.  *
  918.  * Side effects:
  919.  *    None.
  920.  *
  921.  *----------------------------------------------------------------------
  922.  */
  923.  
  924. ENTRY int
  925. GetHostState(spriteID)
  926.     int spriteID;
  927. {
  928.     register Hash_Entry *hashPtr;
  929.     register RecovHostState *hostPtr;
  930.     register int state = RECOV_STATE_UNKNOWN;
  931.     Time time;
  932.  
  933.     LOCK_MONITOR;
  934.  
  935.     hashPtr = Hash_LookOnly(recovHashTable, spriteID);
  936.     if (hashPtr != (Hash_Entry *)NIL) {
  937.     hostPtr = (RecovHostState *)hashPtr->value;
  938.     if (hostPtr != (RecovHostState *)NIL) {
  939.         state = hostPtr->state &
  940.             ~(RECOV_CRASH_CALLBACKS|RECOV_HOST_PINGING);
  941.         if (state == RECOV_HOST_ALIVE) {
  942.         /*
  943.          * Check for recent message traffic before admitting
  944.          * that the other machine is up.
  945.          */
  946.         Timer_GetTimeOfDay(&time, (int *)NIL, (Boolean *)NIL);
  947.         Time_Subtract(time, hostPtr->time, &time);
  948.         if (Time_GT(time, time_TenSeconds)) {
  949.             state = RECOV_STATE_UNKNOWN;
  950.         }
  951.         }
  952.     }
  953.     }
  954.     UNLOCK_MONITOR;
  955.     return(state);
  956. }
  957.  
  958. /*
  959.  *----------------------------------------------------------------------
  960.  *
  961.  * StartPinging --
  962.  *
  963.  *    Make sure there is a background pinging process for the host.
  964.  *    The state bit used to indicate pinging is reset by RpcHostCheck
  965.  *    after it finally gets in a good ping.
  966.  *
  967.  * Results:
  968.  *    None.
  969.  *
  970.  * Side effects:
  971.  *    Starts the pinging callback if not already in progress.
  972.  *
  973.  *----------------------------------------------------------------------
  974.  */
  975.  
  976. ENTRY void
  977. StartPinging(spriteID)
  978.     int spriteID;
  979. {
  980.     register Hash_Entry *hashPtr;
  981.     register RecovHostState *hostPtr;
  982.  
  983.     LOCK_MONITOR;
  984.  
  985.     hashPtr = Hash_LookOnly(recovHashTable, spriteID);
  986.     hostPtr = (RecovHostState *)hashPtr->value;
  987.     if ((hostPtr->state & RECOV_HOST_PINGING) == 0) {
  988.     hostPtr->state |= RECOV_HOST_PINGING;
  989.     Proc_CallFunc(CheckHost, spriteID, 0);
  990.     }
  991.     UNLOCK_MONITOR;
  992. }
  993.  
  994. /*
  995.  *----------------------------------------------------------------------
  996.  *
  997.  * CheckHost --
  998.  *
  999.  *    This is the call back setup when a host is detected as crashed
  1000.  *    and we want to find out when it comes back up.  This pings
  1001.  *    the remote host if it's down or there hasn't been recent traffic.
  1002.  *    A side effect of a successful ping is a call to RpcHostAlive which
  1003.  *    triggers the recovery actions.
  1004.  *
  1005.  * Results:
  1006.  *    None.
  1007.  *
  1008.  * Side effects:
  1009.  *    This will pings the host unless there has been recent message
  1010.  *    traffic.  It reschedules itself if the ping fails.
  1011.  *
  1012.  *----------------------------------------------------------------------
  1013.  */
  1014.  
  1015. static void
  1016. CheckHost(data, callInfoPtr)
  1017.     ClientData data;
  1018.     Proc_CallInfo *callInfoPtr;
  1019. {
  1020.     register int spriteID = (int)data;
  1021.     register int state;
  1022.     ReturnStatus status = SUCCESS;
  1023.  
  1024.     state = GetHostState(spriteID);
  1025.     switch (state) {
  1026.     case RECOV_HOST_DEAD:
  1027.     case RECOV_STATE_UNKNOWN:
  1028.         RECOV_TRACE(spriteID, state, RECOV_CUZ_PING_CHK);
  1029.         status = Rpc_Ping(spriteID);
  1030.         break;
  1031.     case RECOV_HOST_ALIVE:
  1032.         break;
  1033.     }
  1034.     if (status != SUCCESS) {
  1035.     /*
  1036.      * Try again later if the host is still down.
  1037.      */
  1038.     callInfoPtr->interval = rpcPingSeconds * timer_IntOneSecond;
  1039.     } else {
  1040.     StopPinging(spriteID);
  1041.     callInfoPtr->interval = 0;
  1042.     }
  1043. }
  1044.  
  1045. /*
  1046.  *----------------------------------------------------------------------
  1047.  *
  1048.  * StartPinging --
  1049.  *
  1050.  *    Make sure there is a background pinging process for the host.
  1051.  *    The state bit used to indicate pinging is reset by RpcHostCheck
  1052.  *    after it finally gets in a good ping.
  1053.  *
  1054.  * Results:
  1055.  *    None.
  1056.  *
  1057.  * Side effects:
  1058.  *    Starts the pinging callback if not already in progress.
  1059.  *
  1060.  *----------------------------------------------------------------------
  1061.  */
  1062.  
  1063. ENTRY void
  1064. StopPinging(spriteID)
  1065.     int spriteID;
  1066. {
  1067.     register Hash_Entry *hashPtr;
  1068.     register RecovHostState *hostPtr;
  1069.  
  1070.     LOCK_MONITOR;
  1071.  
  1072.     hashPtr = Hash_LookOnly(recovHashTable, spriteID);
  1073.     hostPtr = (RecovHostState *)hashPtr->value;
  1074.     if ((hostPtr->state & RECOV_HOST_PINGING) == 0) {
  1075.     Sys_Panic(SYS_WARNING, "StopPinging found bad state\n");
  1076.     }
  1077.     hostPtr->state &= ~RECOV_HOST_PINGING;
  1078.     UNLOCK_MONITOR;
  1079. }
  1080.  
  1081. /*
  1082.  *----------------------------------------------------------------------
  1083.  *
  1084.  * Rpc_PrintRecovTraceRecord --
  1085.  *
  1086.  *    Format and print the client data part of a recovery trace record.
  1087.  *
  1088.  * Results:
  1089.  *    None.
  1090.  *
  1091.  * Side effects:
  1092.  *    Sys_Printf to the display.
  1093.  *
  1094.  *----------------------------------------------------------------------
  1095.  */
  1096. int
  1097. Rpc_PrintRecovTraceRecord(clientData, event, printHeaderFlag)
  1098.     ClientData clientData;    /* Client data in the trace record */
  1099.     int event;            /* Type, or event, from the trace record */
  1100.     Boolean printHeaderFlag;    /* If TRUE, a header line is printed */
  1101. {
  1102.     RpcRecovTraceRecord *recPtr = (RpcRecovTraceRecord *)clientData;
  1103.     char *name;
  1104.     if (printHeaderFlag) {
  1105.     /*
  1106.      * Print column headers and a newline.
  1107.      */
  1108.     Sys_Printf("%10s %10s %17s\n", "Host", "State", "Event ");
  1109.     }
  1110.     if (clientData != (ClientData)NIL) {
  1111.     Net_SpriteIDToName(recPtr->spriteID, &name);
  1112.     if (name == (char *)NIL) {
  1113.         Sys_Printf("%10d ", recPtr->spriteID);
  1114.     } else {
  1115.         Sys_Printf("%10s ", name);
  1116.     }
  1117.     switch(recPtr->state & ~(RECOV_CRASH_CALLBACKS|RECOV_HOST_PINGING)) {
  1118.         case RECOV_STATE_UNKNOWN:
  1119.         Sys_Printf("%-8s", "Unknown");
  1120.         break;
  1121.         case RECOV_HOST_ALIVE:
  1122.         Sys_Printf("%-8s ", "Alive");
  1123.         break;
  1124.         case RECOV_HOST_DEAD:
  1125.         Sys_Printf("%-8s ", "Dead");
  1126.         break;
  1127.         case RECOV_WAITING:
  1128.         Sys_Printf("%-8s ", "Waiting");
  1129.         break;
  1130.         case RECOV_CRASH:
  1131.         Sys_Printf("%-8s ", "Crash callbacks");
  1132.         break;
  1133.         case RECOV_REBOOT:
  1134.         Sys_Printf("%-8s ", "Reboot callbacks");
  1135.         break;
  1136.     }
  1137.     Sys_Printf("%3s", (recPtr->state & RECOV_CRASH_CALLBACKS) ?
  1138.                 " C " : "   ");
  1139.     Sys_Printf("%3s", (recPtr->state & RECOV_HOST_PINGING) ?
  1140.                 " P " : "   ");
  1141.     switch(event) {
  1142.         case RECOV_CUZ_WAIT:
  1143.         Sys_Printf("waiting");
  1144.         break;
  1145.         case RECOV_CUZ_WAKEUP:
  1146.         Sys_Printf("wakeup");
  1147.         break;
  1148.         case RECOV_CUZ_INIT:
  1149.         Sys_Printf("init");
  1150.         break;
  1151.         case RECOV_CUZ_REBOOT:
  1152.         Sys_Printf("reboot");
  1153.         break;
  1154.         case RECOV_CUZ_CRASH:
  1155.         Sys_Printf("crash");
  1156.         break;
  1157.         case RECOV_CUZ_DONE:
  1158.         Sys_Printf("done");
  1159.         break;
  1160.         case RECOV_CUZ_PING_ASK:
  1161.         Sys_Printf("ping (ask)");
  1162.         break;
  1163.         case RECOV_CUZ_PING_CHK:
  1164.         Sys_Printf("ping (check)");
  1165.         break;
  1166.         case RPC_RECOV_TRACE_STALE:
  1167.         Sys_Printf("stale FS handle");
  1168.         break;
  1169.         default:
  1170.         Sys_Printf("(%x)", event);
  1171.         break;
  1172.     }
  1173.     /* Our caller prints a newline */
  1174.     }
  1175. }
  1176.  
  1177. /*
  1178.  *----------------------------------------------------------------------
  1179.  *
  1180.  * Rpc_PrintRecovTrace --
  1181.  *
  1182.  *    Dump out the recovery trace.  Called via a console L1 keystroke.
  1183.  *
  1184.  * Results:
  1185.  *    None.
  1186.  *
  1187.  * Side effects:
  1188.  *    Prints to the console.
  1189.  *
  1190.  *----------------------------------------------------------------------
  1191.  */
  1192.  
  1193. void
  1194. Rpc_PrintRecovTrace(numRecs)
  1195.     int numRecs;
  1196. {
  1197.     if (numRecs <= 0 || numRecs > rpcRecovTraceLength) {
  1198.     numRecs = rpcRecovTraceLength;
  1199.     }
  1200.     Sys_Printf("RECOVERY TRACE\n");
  1201.     Trace_Print(rpcRecovTraceHdrPtr, numRecs, Rpc_PrintRecovTraceRecord);
  1202. }
  1203.  
  1204.  
  1205.